#include "ct_ringbuffer.h"

static int8_t is_power_of_two(uint16_t x) {
    return (x != 0) && ((x & (x - 1)) == 0);
}

/**
 * @brief Initialize the ring buffer
 * 
 * @param rb Pointer to the ring buffer structure
 * @param buffer Pointer to the data buffer
 * @param size Size of the ring buffer, must be a power of two
 */
void ringbuf_init(RingBuffer_t *rb, int8_t *buffer, uint16_t size) {
        if (!is_power_of_two(size)) {
        // Handle error: size is not a power of two
        return;
    }
    rb->buffer = buffer;
    rb->mask = size - 1;
    rb->put_ptr = 0;
    rb->get_ptr = 0;
}

/**
 * @brief Put a byte into the ring buffer
 * 
 * @param rb Pointer to the ring buffer structure
 * @param byte The byte to be put into the buffer
 * @return int8_t 1 if the operation is successful, 0 if the buffer is full
 */
int8_t ringbuf_put(RingBuffer_t *rb, int8_t byte) {
    if (ringbuf_full(rb)) {
        return 0;
    }
    CC_ACCESS_NOW(int8_t, rb->buffer[rb->put_ptr]) = byte;
    CC_ACCESS_NOW(int8_t, rb->put_ptr) = (rb->put_ptr + 1) & rb->mask;
    
    return 1;
}

/**
 * @brief Get a byte from the ring buffer
 * 
 * @param rb Pointer to the ring buffer structure
 * @return int8_t The byte retrieved from the buffer, -1 if the buffer is empty
 */
char ringbuf_get(RingBuffer_t *rb, int8_t *byte) {
    if (ringbuf_empty(rb)) {
        return -1;
    }
    *byte = CC_ACCESS_NOW(int8_t, rb->buffer[rb->get_ptr]);
    CC_ACCESS_NOW(int8_t, rb->get_ptr) = (rb->get_ptr + 1) & rb->mask;
    return 1;
}

/**
 * @brief Check if the ring buffer is full
 * 
 * @param rb Pointer to the ring buffer structure
 * @return int8_t 1 if the buffer is full, 0 otherwise
 */
int8_t ringbuf_full(const RingBuffer_t *rb) {
    return ((rb->put_ptr - rb->get_ptr) & rb->mask) == rb->mask;
}

/**
 * @brief Check if the ring buffer is empty
 * 
 * @param rb Pointer to the ring buffer structure
 * @return int8_t 1 if the buffer is empty, 0 otherwise
 */
int8_t ringbuf_empty(const RingBuffer_t *rb) {
    return rb->put_ptr == rb->get_ptr;
}

/**
 * @brief Return the size of the ring buffer
 * 
 * @param rb Pointer to the ring buffer structure
 * @return int8_t The size of the ring buffer
 */
int8_t ringbuf_size(const RingBuffer_t *rb) {
    return rb->mask + 1;
}

/**
 * @brief Return the number of elements in the ring buffer
 * 
 * @param rb Pointer to the ring buffer structure
 * @return int8_t The number of elements in the ring buffer
 */
int8_t ringbuf_elements(const RingBuffer_t *rb) {
    return (rb->put_ptr - rb->get_ptr) & rb->mask;
}


/* Example usage of the ring buffer */
/*
void test_ringbuffer(void) {
    int8_t buffer[RINGBUFFER_SIZE];
    RingBuffer_t rb;

    RingBuffer_Init(&rb, buffer, RINGBUFFER_SIZE);

    // Producer: Put data into the ring buffer
    if (RingBuffer_Put(&rb, data) == 0) {
        // Handle buffer full
    }

    // Consumer: Get data from the ring buffer
    int8_t data = RingBuffer_Get(&rb);
    if (data != -1) {
        // Process data
    }

    // Check if the ring buffer is empty
    if (ringbuf_empty(&rb)) {
        // Handle buffer empty
    }

    // Check if the ring buffer is full
    if (ringbuf_full(&rb)) {
        // Handle buffer full
    }
}
*/